package gis.uzd_1;

import java.awt.Frame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.prefs.Preferences;

import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.event.MenuEvent;
import javax.swing.event.MenuListener;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;

import org.geotools.data.FeatureSource;
import org.geotools.data.FileDataStore;
import org.geotools.data.FileDataStoreFinder;
import org.geotools.feature.FeatureCollection;
import org.geotools.swing.table.FeatureCollectionTableModel;

import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;

/**
 * Application menu.
 * 
 * @version 0.1
 * @date 2010-04-19
 * @author Edmundas Matusevicius
 */
public class GISMenu extends JMenuBar {
    public GISMenu() {
        setUpMenus();
        setUpActions();
    }
    
    private void setUpMenus() {
        
        // set mnemonics
        
        layerMenu.setMnemonic('L');
        viewMenu.setMnemonic('V');
        objectMenu.setMnemonic('O');
        taskMenu.setMnemonic('T');
        
        addItem.setMnemonic('A');
        removeItem.setMnemonic('R');
        showItem.setMnemonic('S');
        hideItem.setMnemonic('H');
        detailsLayerItem.setMnemonic('D');
        
        zoomInItem.setMnemonic('I');
        zoomOutItem.setMnemonic('O');
        zoomExtentViewItem.setMnemonic('E');
        moveItem.setMnemonic('M');
        
        selectItem.setMnemonic('S');
        detailsObjectItem.setMnemonic('D');
        zoomExtentObjectItem.setMnemonic('E');
        
        findTowersItem.setMnemonic('F');
        
        // add menus
        
        add(layerMenu);
        add(viewMenu);
        add(objectMenu);
        add(taskMenu);
        
        // add Layer sub menus
        
        layerMenu.add(addItem);
        layerMenu.add(removeItem);
        layerMenu.addSeparator();
        layerMenu.add(showItem);
        layerMenu.add(hideItem);
        layerMenu.addSeparator();
        layerMenu.add(detailsLayerItem);
        
        // add View sub menus
        
        viewMenu.add(zoomInItem);
        viewMenu.add(zoomOutItem);
        viewMenu.addSeparator();
        viewMenu.add(zoomExtentViewItem);
        viewMenu.addSeparator();
        viewMenu.add(moveItem);
        
        // add Object sub menus
        
        objectMenu.add(selectItem);
        objectMenu.add(detailsObjectItem);
        objectMenu.addSeparator();
        objectMenu.add(zoomExtentObjectItem);
        
        // add Task sub menus
        
        taskMenu.add(findTowersItem);
    }
    
    private void setUpActions() {
        
        // add menus action listeners
        
        layerMenu.addMenuListener(new MenuListener() {
            public void menuCanceled(MenuEvent e) {
                
            }
            
            public void menuDeselected(MenuEvent e) {
                DataPanel.mapPanel.setRepaint(true);
            }
            
            @SuppressWarnings("static-access")
            public void menuSelected(MenuEvent e) {
                DataPanel.mapPanel.setRepaint(false);
                
                if (DataPanel.layerPanel.getSelectionCount() > 0) {
                    removeItem.setEnabled(true);
                    showItem.setEnabled(true);
                    hideItem.setEnabled(true);
                    
                    if (DataPanel.layerPanel.getSelectionPath().getLastPathComponent().toString() != DataPanel.layerPanel.dataRoot.get(0).toString()) {
                        detailsLayerItem.setEnabled(true);
                    }
                } else {
                    removeItem.setEnabled(false);
                    showItem.setEnabled(false);
                    hideItem.setEnabled(false);
                    detailsLayerItem.setEnabled(false);
                }
            }
        });
        
        viewMenu.addMenuListener(new MenuListener() {
            public void menuCanceled(MenuEvent e) {
                
            }
            
            public void menuDeselected(MenuEvent e) {
                DataPanel.mapPanel.setRepaint(true);
            }
            
            @SuppressWarnings("static-access")
            public void menuSelected(MenuEvent e) {
                DataPanel.mapPanel.setRepaint(false);
                
                if (DataPanel.mapPanel.mapContext.getLayerCount() > 0) {
                    zoomInItem.setEnabled(true);
                    zoomOutItem.setEnabled(true);
                    zoomExtentViewItem.setEnabled(true);
                    moveItem.setEnabled(true);
                } else {
                    zoomInItem.setEnabled(false);
                    zoomOutItem.setEnabled(false);
                    zoomExtentViewItem.setEnabled(false);
                    moveItem.setEnabled(false);
                }
            }
        });
        
        objectMenu.addMenuListener(new MenuListener() {
            public void menuCanceled(MenuEvent e) {
                
            }
            
            public void menuDeselected(MenuEvent e) {
                DataPanel.mapPanel.setRepaint(true);
            }
            
            @SuppressWarnings("static-access")
            public void menuSelected(MenuEvent e) {
                DataPanel.mapPanel.setRepaint(false);
                
                if (DataPanel.layerPanel.getSelectionCount() > 0) {
                    if (DataPanel.layerPanel.getSelectionPath().getLastPathComponent().toString() != DataPanel.layerPanel.dataRoot.get(0).toString()) {
                        selectItem.setEnabled(true);
                        
                        if (DataPanel.mapPanel.selectedFeatures != null) {
                            detailsObjectItem.setEnabled(true);
                            zoomExtentObjectItem.setEnabled(true);
                        } else {
                            detailsObjectItem.setEnabled(false);
                            zoomExtentObjectItem.setEnabled(false);
                        }
                    } else {
                        if (DataPanel.mapPanel.selectedFeatures != null) {
                            selectItem.setEnabled(false);
                            detailsObjectItem.setEnabled(false);
                            zoomExtentObjectItem.setEnabled(true);
                        } else {
                            selectItem.setEnabled(false);
                            detailsObjectItem.setEnabled(false);
                            zoomExtentObjectItem.setEnabled(false);
                        }
                    }
                } else {
                    selectItem.setEnabled(false);
                    detailsObjectItem.setEnabled(false);
                    zoomExtentObjectItem.setEnabled(false);
                }
            }
        });
        
        taskMenu.addMenuListener(new MenuListener() {
            public void menuCanceled(MenuEvent e) {
                
            }
            
            public void menuDeselected(MenuEvent e) {
                DataPanel.mapPanel.setRepaint(true);
            }
            
            @SuppressWarnings("static-access")
            public void menuSelected(MenuEvent e) {
                DataPanel.mapPanel.setRepaint(false);
                
                // TODO: comment
                if (DataPanel.layerPanel.getSelectionCount() > 0) {
                    String selectedLayer = DataPanel.layerPanel.getSelectionPath().getLastPathComponent().toString();
                    if (selectedLayer != DataPanel.layerPanel.dataRoot.get(0).toString()) {
                        if (selectedLayer.equals("Apskrity") || selectedLayer.equals("Rajonai")) {
                            if (DataPanel.mapPanel.selectedFeatures != null) {
                                findTowersItem.setEnabled(true);
                            } else {
                                findTowersItem.setEnabled(false);
                            }
                        } else {
                            findTowersItem.setEnabled(false);
                        }
                    }
                } else {
                    findTowersItem.setEnabled(false);
                }
            }
        });
        
        // add Layer sub menus action listeners
        
        addItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                addLayer();
            }
        });
        
        removeItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                removeLayer();
            }
        });
        
        showItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                setMapLayerVisible(true);
            }
        });
        
        hideItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                setMapLayerVisible(false);
            }
        });
        
        detailsLayerItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                showDetailFrame(true);
            }
        });
        
        // add View sub menus action listeners
        
        zoomInItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                if (viewState == ViewState.ZOOM_IN)
                    viewState = null;
                else
                    viewState = ViewState.ZOOM_IN;
            }
        });
        
        zoomOutItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                if (viewState == ViewState.ZOOM_OUT)
                    viewState = null;
                else
                    viewState = ViewState.ZOOM_OUT;
            }
        });
        
        zoomExtentViewItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                DataPanel.mapPanel.reset();
            }
        });
        
        moveItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                if (viewState == ViewState.MOVE)
                    viewState = null;
                else
                    viewState = ViewState.MOVE;
            }
        });
        
        // add Object sub menus action listeners
        
        selectItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                if (viewState == ViewState.SELECT)
                    viewState = null;
                else
                    viewState = ViewState.SELECT;
            }
        });
        
        detailsObjectItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                showDetailFrame(false);
            }
        });
        
        zoomExtentObjectItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                DataPanel.mapPanel.showSelectedFeatures();
            }
        });
        
        // add Task sub menus action listeners
        
        findTowersItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                // Mock up data
                
                TowerLogic towerLogic = new TowerLogic();
                towerLogic.setUpMockUpData();
                towerLogic.run();
                
                // TODO: comment
//                Frame[] aFrames = JFrame.getFrames();
//                
//                for (Frame frm : aFrames) {
//                    if (frm.getClass().getSimpleName().equals("MainFrame")) {
//                        TowerDialog towerDialog = new TowerDialog((JFrame) frm); 
//                        towerDialog.setVisible(true);
//                    }
//                }
            }
        });
    }
    
    // manipulation methods
    
    private void addLayer() {
        // get browse directory
        String dir = node.get("dir", ".");
        
        JFileChooser fileChooser = new JFileChooser();
        
        // set file chooser options
        
        fileChooser.setCurrentDirectory(new File(dir));
        
        fileChooser.setAcceptAllFileFilterUsed(false);
        fileChooser.addChoosableFileFilter(new FileNameExtensionFilter("AUX files (*.aux)", "aux"));
        fileChooser.addChoosableFileFilter(new FileNameExtensionFilter("Shape files (*.shp)", "shp"));
        
        fileChooser.setMultiSelectionEnabled(true);
        
        int result = fileChooser.showOpenDialog(null);
        
        // set browse directory
        node.put("dir", fileChooser.getCurrentDirectory().toString());
        
        if (result == JFileChooser.APPROVE_OPTION) {
            File[] files = fileChooser.getSelectedFiles();
            
            for (int i = 0; i < files.length; i++) {
                try {
                    // prepare file name
                    
                    String fileName = files[i].getName();
                    fileName = fileName.substring(0, fileName.indexOf("."));
                    fileName = fileName.toLowerCase();
                    
                    String firstChar = fileName.substring(0, 1).toUpperCase();
                    
                    fileName = firstChar.concat(fileName.substring(1));
                    
                    // add to map
                    
                    FileDataStore dataStore = FileDataStoreFinder.getDataStore(files[i]);
                    FeatureSource<SimpleFeatureType, SimpleFeature> featureSource = dataStore.getFeatureSource();
                    
                    GISMap.mapContext.addLayer(featureSource, null);
                    GISMap.mapContext.getLayer(GISMap.mapContext.getLayerCount() - 1).setTitle(fileName);
                    
                    // add to layer list
                    
                    GISLayer.dataNode.get(0).add(new DefaultMutableTreeNode(fileName));
                    GISLayer.dataRoot.get(0).add(GISLayer.dataNode.get(0).get(GISLayer.dataNode.get(0).size() - 1));
                    
                    // check new layer
                    GISLayer.checkingModel.addCheckingPath(new TreePath(GISLayer.dataNode.get(0).get(GISLayer.dataNode.get(0).size() - 1).getPath()));
                    
                    // refresh component
                    
                    DataPanel.layerPanel.expandAll();
                    DataPanel.layerPanel.updateUI();
                } catch (IOException e) {
//                    e.printStackTrace();
                }
            }
        }
    }
    
    private void removeLayer() {
        TreePath selectedPath = DataPanel.layerPanel.getSelectionPath();
        
        if (selectedPath != null) {
            DefaultMutableTreeNode root = GISLayer.dataRoot.get(0);
            
            // selected root
            if (selectedPath.equals(new TreePath(root.getPath()))) {
                // find all child
                for (int j = 0; j < root.getChildCount(); j++) {
                    TreeNode node = root.getChildAt(j);
                    
                    // go through all map layers
                    for (int k = 0; k < GISMap.mapContext.getLayerCount(); k++) {
                        if (node.toString() == GISMap.mapContext.getLayer(k).getTitle()) {
                            GISMap.mapContext.removeLayer(k);
                            break;
                        }
                    }
                }
                root.removeAllChildren();
                
                // check root
                GISLayer.checkingModel.addCheckingPath(new TreePath(GISLayer.dataRoot.get(0).getPath()));
                
            // selected node
            } else {
                // remove from map
                for (int j = 0; j < GISMap.mapContext.getLayerCount(); j++) {
                    if (selectedPath.getLastPathComponent().toString() == GISMap.mapContext.getLayer(j).getTitle()) {
                        GISMap.mapContext.removeLayer(j);
                        break;
                    }
                }
                
                // remove from layers list
                for (int j = 0; j < root.getChildCount(); j++) {
                    if (selectedPath.getLastPathComponent().toString() == root.getChildAt(j).toString()) {
                        root.remove(j);
                        break;
                    }
                }
            }
            
            // refresh components
            
            DataPanel.layerPanel.expandAll();
            DataPanel.layerPanel.updateUI();
            
            DataPanel.mapPanel.updateUI();
        }
    }
    
    private void setMapLayerVisible(boolean visible) {
        TreePath selectedPath = DataPanel.layerPanel.getSelectionPath();
        
        if (selectedPath != null) {
            ArrayList<DefaultMutableTreeNode> nodes = GISLayer.dataNode.get(0);
            // selected root
            if (selectedPath.equals(new TreePath(GISLayer.dataRoot.get(0).getPath()))) {
                // find all child
                for (int j = 0; j < nodes.size(); j++) {
                    TreeNode node = nodes.get(j);
                    
                    // go through all map layers
                    for (int k = 0; k < GISMap.mapContext.getLayerCount(); k++) {
                        if (node.toString() == GISMap.mapContext.getLayer(k).getTitle()) {
                            GISMap.mapContext.getLayer(k).setVisible(visible);
                            break;
                        }
                    }
                    
                    if (visible)
                        GISLayer.checkingModel.addCheckingPath(new TreePath(nodes.get(j).getPath()));
                    else
                        GISLayer.checkingModel.removeCheckingPath(new TreePath(nodes.get(j).getPath()));
                }
                
                // check root
                if (visible)
                    GISLayer.checkingModel.addCheckingPath(new TreePath(GISLayer.dataRoot.get(0).getPath()));
                else
                    GISLayer.checkingModel.removeCheckingPath(new TreePath(GISLayer.dataRoot.get(0).getPath()));
            // selected node
            } else {
                // show in map
                for (int j = 0; j < GISMap.mapContext.getLayerCount(); j++) {
                    if (selectedPath.getLastPathComponent().toString() == GISMap.mapContext.getLayer(j).getTitle()) {
                        GISMap.mapContext.getLayer(j).setVisible(visible);
                        break;
                    }
                }
                
                // check in layers list
                for (int j = 0; j < nodes.size(); j++) {
                    if (selectedPath.getLastPathComponent().toString() == nodes.get(j).toString()) {
                        if (visible)
                            GISLayer.checkingModel.addCheckingPath(new TreePath(nodes.get(j).getPath()));
                        else
                            GISLayer.checkingModel.removeCheckingPath(new TreePath(nodes.get(j).getPath()));
                        break;
                    }
                }
            }
        }
    }
    
    @SuppressWarnings({ "unchecked", "static-access" })
    private void showDetailFrame(boolean allFeatures) {
        TreePath selectedPath = DataPanel.layerPanel.getSelectionPath();
        
        if (selectedPath != null) {
            if (!selectedPath.equals(new TreePath(GISLayer.dataRoot.get(0).getPath()))) {
                for (int j = 0; j < GISMap.mapContext.getLayerCount(); j++) {
                    if (selectedPath.getLastPathComponent().toString() == GISMap.mapContext.getLayer(j).getTitle()) {
                        try {
                            FeatureSource source = GISMap.mapContext.getLayer(j).getFeatureSource();
                            FeatureCollectionTableModel model = null;
                            
                            if (allFeatures) {
                                FeatureCollection<SimpleFeatureType, SimpleFeature> features = source.getFeatures();
                                model = new FeatureCollectionTableModel(features);
                            } else {
                                model = new FeatureCollectionTableModel(DataPanel.mapPanel.selectedFeatures);
                            }
                            
                            DetailPanel.source = source;
                            DetailPanel.table.setModel(model);
                            
                            DetailFrame detailFrame = new DetailFrame();
                            detailFrame.setVisible(true);
                        } catch (Exception e) {
//                            e.printStackTrace();
                        }
                        
                        break;
                    }
                }
            }
        }
    }
    
    // menus
    
    private JMenu layerMenu = new JMenu("Layer");
    private JMenu viewMenu = new JMenu("View");
    private JMenu objectMenu = new JMenu("Object");
    private JMenu taskMenu = new JMenu("Task");
    
    // Layer sub menus
    
    protected JMenuItem addItem = new JMenuItem("Add");
    protected JMenuItem removeItem = new JMenuItem("Remove");
    protected JMenuItem showItem = new JMenuItem("Show");
    protected JMenuItem hideItem = new JMenuItem("Hide");
    protected JMenuItem detailsLayerItem = new JMenuItem("Details");
    
    // View sub menus
    
    private JMenuItem zoomInItem = new JMenuItem("Zoom in");
    private JMenuItem zoomOutItem = new JMenuItem("Zoom out");
    private JMenuItem zoomExtentViewItem = new JMenuItem("Zoom to extent");
    private JMenuItem moveItem = new JMenuItem("Move");
    
    // Object sub menus
    
    private JMenuItem selectItem = new JMenuItem("Select");
    private JMenuItem detailsObjectItem = new JMenuItem("Details");
    private JMenuItem zoomExtentObjectItem = new JMenuItem("Zoom to extent");
    
    // Task sub menus
    
    private JMenuItem findTowersItem = new JMenuItem("Find towers");
    
    // application preferences
    
    private Preferences root = Preferences.userRoot();
    private final Preferences node = root.node("/gis/uzd_1");
    
    // menu tools
    
    static ViewState viewState;
    static enum ViewState { ZOOM_IN, ZOOM_OUT, MOVE, SELECT };
}
